#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/select.h>

#define SERPORT 8001

int main(int argc, char* argv[]){
    //创建一个套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd < 0){
        perror("socket error");
        exit(1);
    }
    //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    struct sockaddr_in seraddr, cliaddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERPORT);
    //192.168.0.184  主机字节序的字符串
    seraddr.sin_addr.s_addr = INADDR_ANY;//网络字节序的整形 INADDR_ANY = 0
    //给套接字绑定了IP+port
    int bret = bind(lfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if(bret < 0){
        perror("bind error");
        exit(1);
    }
    int lret = listen(lfd, 64);
    printf("listen is return\n");
    //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    socklen_t addrlen = sizeof(cliaddr);
    //从全连接队列取出一个连接，返回的值是和客户端通信的socket的文件描述符
    //我们希望select帮我门监听哪个文件描述符的哪个事件  lfd的读事件
    //定义位图
    fd_set rset, aset;
    //初始化位图的每一个位为0
    FD_ZERO(&aset);
    //将lfd对应的位图中的位 置1
    FD_SET(lfd, &aset);
    int maxfd = lfd;

    char buf[1024];

    while(1){
        rset = aset;
        //select帮助我们阻塞监听,select的返回值：有多少个监听的事件触发了
        int sret = select(maxfd + 1, &rset, NULL, NULL, NULL);
        if(sret == -1){
            perror("select error");
            exit(1);
        }
        //select返回的时候，我们需要寻找哪些事件触发了，然后把他处理掉
        //两类文件描述符：lfd, cfd
        
        //先判断lfd的事件有没有触发
        if(FD_ISSET(lfd, &rset)){
            int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &addrlen);
            if(cfd < 0){
                perror("accept error");
                exit(1);
            }
            printf("新的客户端建立连接\n");
            //新增加的cfd也需要交给select监听
            FD_SET(cfd, &aset);
            if(maxfd < cfd){
                maxfd = cfd;
            }
        }
        //便利cfd最小值到最大值之间
        for(int i = lfd + 1; i < maxfd + 1; i++){
            if(FD_ISSET(i, &rset)){
                //i客户端的读事件触发了
                int rret = read(i, buf, sizeof(buf));
                if(rret == -1){
                    perror("read error");
                    exit(1);
                }
                else if(rret == 0){
                    printf("客户端断开连接\n");
                    FD_CLR(i, &aset);
                    close(i);
                }
                else{
                    write(i, buf, rret);
                    write(1, buf, rret);
                }
            }
        }        
    }
    return 0;
}
